import joblib
import numpy as np
import os

from ..features.feature_engineer import FeatureEngineer


class ScorePredictor:
    """
    成绩预测器 - 使用训练好的模型进行预测
    直接集成 FeatureEngineer 确保特征一致性
    """

    def __init__(self, model_path=None):
        self.model = None
        self.feature_engineer = FeatureEngineer()
        self.feature_columns = self._get_feature_columns()
        self.model_loaded = False
        self.model_path = model_path or 'ml/models/score_predictor.pkl'

        # 初始化时加载模型
        self.load_model()

    def _get_feature_columns(self):
        """
        从 FeatureEngineer 动态获取特征列
        确保训练和预测使用相同的特征顺序
        """
        categories = self.feature_engineer.get_feature_categories()
        # 扁平化所有特征类别
        all_features = []
        for category_features in categories.values():
            all_features.extend(category_features)

        # 去重并排序，确保顺序一致性
        unique_features = list(set(all_features))
        unique_features.sort()  # 按字母顺序排序

        print(f"📊 预测器特征列: {len(unique_features)} 个特征")
        return unique_features

    def load_model(self):
        """加载训练好的模型"""
        try:
            if not os.path.exists(self.model_path):
                print(f"⚠️ 模型文件不存在: {self.model_path}")
                self.model_loaded = False
                return False

            # 加载模型
            model_info = joblib.load(self.model_path)
            self.model = model_info['model']
            self.model_loaded = True

            print(f"✅ 预测模型加载成功")
            print(f"   - 模型类型: {type(self.model).__name__}")
            print(f"   - 特征数量: {len(self.feature_columns)}")
            print(f"   - 模型路径: {self.model_path}")

            # 🎯 验证特征一致性
            self._validate_feature_compatibility(model_info)

            return True

        except Exception as e:
            print(f"❌ 模型加载失败: {str(e)}")
            self.model_loaded = False
            return False

    def _validate_feature_compatibility(self, model_info):
        """验证预测器特征与训练时特征的一致性"""
        saved_features = model_info.get('feature_columns', [])

        if set(self.feature_columns) != set(saved_features):
            print("⚠️ 特征不一致警告:")
            print(f"   - 预测器特征: {len(self.feature_columns)} 个")
            print(f"   - 训练时特征: {len(saved_features)} 个")

            # 找出差异
            missing_in_predictor = set(saved_features) - set(self.feature_columns)
            extra_in_predictor = set(self.feature_columns) - set(saved_features)

            if missing_in_predictor:
                print(f"   - 预测器缺少特征: {missing_in_predictor}")
            if extra_in_predictor:
                print(f"   - 预测器多余特征: {extra_in_predictor}")
        else:
            print("✅ 特征一致性验证通过")

    def predict(self, features):
        """
        使用模型进行预测

        Args:
            features: 特征字典，来自 FeatureEngineer.extract_features()

        Returns:
            dict: 预测结果
        """
        if not self.model_loaded:
            return {
                'success': False,
                'error': '模型未加载，请先训练模型',
                'fallback_to_rules': True
            }

        try:
            # 🎯 使用动态特征列顺序进行向量化
            feature_vector = self._features_to_vector(features)

            # 执行预测
            predicted_score = self.model.predict([feature_vector])[0]

            # 计算置信度
            confidence = self._calculate_confidence(features)

            # 获取重要特征
            important_features = self._get_important_features(features)

            return {
                'success': True,
                'predicted_score': float(predicted_score),
                'confidence': float(confidence),
                'model_type': type(self.model).__name__,
                'features_used': len(feature_vector),
                'important_features': important_features,
                'feature_columns': self.feature_columns  # 调试信息
            }

        except Exception as e:
            print(f"❌ 预测失败: {str(e)}")
            return {
                'success': False,
                'error': str(e),
                'fallback_to_rules': True
            }

    def _features_to_vector(self, features):
        """将特征字典转换为特征向量"""
        vector = []
        for column in self.feature_columns:
            # 使用特征工程计算的特征值，缺失值用0填充
            vector.append(features.get(column, 0.0))

        return np.array(vector)

    def _calculate_confidence(self, features):
        """计算预测置信度"""
        base_confidence = 0.8

        # 根据数据质量调整置信度
        exam_count = features.get('exam_count', 0)
        if exam_count == 0:
            base_confidence *= 0.5  # 无历史数据，置信度低
        elif exam_count < 3:
            base_confidence *= 0.8  # 数据少，置信度中等

        # 根据成绩稳定性调整
        volatility = features.get('volatility', 0)
        if volatility > 15:  # 波动大，置信度降低
            base_confidence *= 0.9

        # 根据特征完整性调整
        missing_features = sum(1 for col in self.feature_columns if col not in features)
        if missing_features > 0:
            base_confidence *= (1 - missing_features / len(self.feature_columns) * 0.5)

        return max(0.1, min(1.0, base_confidence))

    def _get_important_features(self, features):
        """获取对本次预测重要的特征"""
        if not hasattr(self.model, 'feature_importances_'):
            return []

        try:
            # 获取特征重要性排名前5的特征
            important_indices = np.argsort(self.model.feature_importances_)[-5:][::-1]
            important_features = []

            for idx in important_indices:
                if idx < len(self.feature_columns):
                    feature_name = self.feature_columns[idx]
                    importance = self.model.feature_importances_[idx]
                    value = features.get(feature_name, 0)

                    important_features.append({
                        'name': feature_name,
                        'importance': float(importance),
                        'value': float(value),
                        'description': self._get_feature_description(feature_name)
                    })

            return important_features
        except Exception as e:
            print(f"⚠️ 获取重要特征失败: {e}")
            return []

    def _get_feature_description(self, feature_name):
        """获取特征描述"""
        descriptions = {
            'avg_score': '平均成绩',
            'recent_score': '最近成绩',
            'score_trend': '成绩趋势',
            'volatility': '成绩波动性',
            'exam_count': '考试次数',
            'first_score': '首次成绩',
            'stability_ratio': '稳定性比率'
        }
        return descriptions.get(feature_name, feature_name)

    def is_ready(self):
        """检查预测器是否就绪"""
        return self.model_loaded

    def get_model_info(self):
        """获取模型信息"""
        if not self.model_loaded:
            return {
                "status": "not_loaded",
                "model_path": self.model_path,
                "feature_count": len(self.feature_columns),
                "message": "请先运行训练命令生成模型"
            }

        return {
            "status": "loaded",
            "model_type": type(self.model).__name__,
            "feature_count": len(self.feature_columns),
            "model_path": self.model_path,
            "features_sample": self.feature_columns[:5]  # 显示前5个特征
        }

    def validate_features(self, features):
        """验证输入特征是否完整"""
        missing_features = set(self.feature_columns) - set(features.keys())
        if missing_features:
            print(f"⚠️ 缺失特征: {missing_features}")
            return False
        return True


# 创建全局实例
score_predictor = ScorePredictor()